package com.tanhua.sso.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.sso.mapper.LogMapper;
import com.tanhua.sso.mapper.UserMapper;
import com.tanhua.sso.pojo.Log;
import com.tanhua.sso.pojo.User;
import com.tanhua.sso.pojo.UserInfo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.impl.client.NoopUserTokenHandler;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName :  UserService
 * @Author : Tenebrous
 * @Date: 2020/12/8 14:22
 * @Description : 用户登录校验的业务层
 */
@Service
public class UserService {

    /**
     * 注入redisTemplate模板
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 注入usermapper
     */
    @Autowired
    private UserMapper userMapper;

    /**
     * 注入加密盐
     */
    @Value("${jwt.secret}")
    private String secret;

    /**
     *  jackson的mapper对象
     */
    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);


    /**
     * rocketMQ模板
     */
    @Autowired
    private RocketMQTemplate rocketMqTemplate;

    @Autowired
    private HuanXinService huanXinService;

    @Autowired
    private UserService userService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private LogService logService;

    @Autowired
    private LogMapper logMapper;

    @Autowired
    private UserFreezeService userFreezeService;

    /**
     * 登录逻辑
     *      1、获取redis中的验证码与用户输入的验证码是否一致
     *      2、判断手机号是否注册，如果注册过则登录成功，否则直接注册
     *      3、生成token
     *      4、生成token后发现消息
     * @param mobile 手机号
     * @param code  状态码
     * @return      如果校验成功返回token，失败返回null
     */
    public String login(String mobile, String code) {
        // ------------------------------1、判断验证码是否一致----------------------------------------
        // 拼delete接验证码的key
        String redisKey = "CHECK_CODE_" + mobile;
        // 从redis中根据key去获取验证码
        String value = this.redisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isEmpty(value)) {
            // 如果从redis中取出的验证码为空，说明验证码失效
            return null;
        }
        if (!StringUtils.equals(value,code)) {
            // 如果从redis中取出的验证码与用户输入的不一致，验证码输入错误
            return null;
        }

        // 验证码正确,删除之前的验证码
        this.redisTemplate.delete(redisKey);
        // --------------------------------2、判断手机号是否被注册---------------------------------------------
        // 默认是已注册的手机号(标记位思想)
        boolean isNew = false;
        // 校验该手机号是否已经注册, 如果没有注册，需要注册一个账号，如果已经注册，直接登录
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", mobile);
        User user = this.userMapper.selectOne(queryWrapper);
        if (null == user) {
            // 如果为空，说明改手机号未注册，需要注册信息
            user = new User();
            user.setMobile(mobile);
            // 默认密码为123456，采用md5加密
            user.setPassword(DigestUtils.md5Hex("123456"));
            // 直接向数据库插入数据
            this.userMapper.insert(user);
            isNew = true;

            // 注册环信用户
            this.huanXinService.register(user.getId());
        }
        String cacheKey = "FREEZE_" + user.getId();
        String userStatus = this.redisTemplate.opsForValue().get(cacheKey);
        System.out.println(userStatus);
        Integer type = userFreezeService.queryFreezingRange(user.getId());
        if (type == 1){
            return null;
        }
        // --------------------------------3、生成token---------------------------------------------
        // 将手机号和用户id封装成map集合，然后生成token
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("mobile", mobile);
        claims.put("id", user.getId());

        // 生成token
        String token = Jwts.builder()
                // 设置响应数据体
                .setClaims(claims)
                // 设置加密方法和加密盐
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();
        try {
            // 将token存入redis中
            String redisTokenKey = "TOKEN_" + token;
            String redisTokenValue = MAPPER.writeValueAsString(user);
            this.redisTemplate.opsForValue().set(redisTokenKey, redisTokenValue, Duration.ofHours(1));
        } catch (Exception e) {
            LOGGER.error("存储token出错", e);
            return null;
        }
        Log logs = logMapper.selectById(user.getId());
        if(logs != null){
            Log log = new Log();
            log.setLogTime(System.currentTimeMillis());
            QueryWrapper<Log> querys = new QueryWrapper<>();
            querys.eq("id", user.getId());
            this.logMapper.update(log,querys);
        }else {
        Log log = new Log();
        log.setId(user.getId());
        log.setLogTime(System.currentTimeMillis());
        log.setPlace("北京");
        log.setEquipment("华为手机");
        log.setType("01");
        this.logMapper.insert(log);
        }
        // --------------------------------4、生成token后发现消息---------------------------------------------
        // 发送消息
        try {
            // 将信息封装成map集合
            Map<String, Object> msg = new HashMap<>(3);
            msg.put("id", user.getId());
            msg.put("mobile", mobile);
            // 登录时间
            msg.put("date", System.currentTimeMillis());
            // 参数：指定的消息队列， 消息
            this.rocketMqTemplate.convertAndSend("tanhua-sso-login", msg);
        } catch (Exception e) {
            LOGGER.error("发送消息出错", e);
            return null;
        }
        //this.logService.saveLog(token);
        return isNew + "|" + token;
    }

    /**
     * 根据用户token去查询信息
     * @param token 用户信息
     * @return 返回数据库中的用户信息
     */
    public User queryUserByToken(String token) {
        try {
            // 拼接token
            String redisTokenKey = "TOKEN_" + token;
            // 从redis中以"TOKEN_" + token 为key去取值
            String cacheData = this.redisTemplate.opsForValue().get(redisTokenKey);
            if (StringUtils.isEmpty(cacheData)) {
                // 如果没有，返回null
                return null;
            }
            // 刷新token的存活时间，延长一小时
            this.redisTemplate.expire(redisTokenKey, 1, TimeUnit.HOURS);
            // 将用户信息反序列化
            return MAPPER.readValue(cacheData, User.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    public boolean sendVerificationCode(String token) {
        // 查询到用户的手机号
        User user = this.userService.queryUserByToken(token);
        if (null == user) {
            return false;
        }
        Map<String, Object> sendCheckCode = this.smsService.sendCheckCode(user.getMobile());
        int code = (Integer) sendCheckCode.get("code");
        return code == 3;
    }

    public Boolean checkVerificationCode(String code, String token) {
        //查询到用户的手机号
        User user = this.userService.queryUserByToken(token);
        if (null == user) {
            return false;
        }

        String redisKey = SmsService.REDIS_KEY_PREFIX + user.getMobile();
        String value = this.redisTemplate.opsForValue().get(redisKey);

        if (StringUtils.equals(value, code)) {
            //验证码正确
            this.redisTemplate.delete(redisKey);
            return true;
        }

        return false;
    }

    public boolean savePhone(String token, String newPhone) {
        User user = this.userService.queryUserByToken(token);
        return this.userService.updatePhone(user.getId(), newPhone);
    }

    public boolean updatePhone(Long id, String newPhone) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", newPhone);
        User user = this.userMapper.selectOne(queryWrapper);
        if (user != null) {
            // 改手机号已经存在
            return false;
        }
        user = new User();
        user.setId(id);
        user.setMobile(newPhone);
        return this.userMapper.updateById(user) > 0;
    }
}
